package com.lry.pda.bottom.mysql.frontend;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Production {

    //点号在表达式右边的位置，0代表是在右边的最左端，例如t -> .f
    private int dotPos;

    //表达式左边的符号
    private int left;

    //表达式右边的符号集合
    private List<Integer> rights;

    //表达式进行reduce，当前输入在lookAhead就可以进行reduce操作
    private Set<Integer> lookAhead = new HashSet<>();

    //表达式编号
    private int number;

    public Production(int dotPos,int left,List<Integer> rights,int number){
        if(dotPos>=rights.size()){
            dotPos = rights.size();
        }
        this.dotPos = dotPos;
        this.left = left;
        this.rights = rights;
        this.number = number;

        lookAhead.add(MysqlTokenType.EOI.ordinal());
    }


    public int getLeft() {
        return left;
    }

    public List<Integer> getRights() {
        return rights;
    }

    /**
     * 返回表达式右边点号右边的第一个符号
     * 例如：s -> .e
     * 返回e
     * @return
     */
    public int getDotSymbol() {
        if(dotPos>=rights.size()){
            return MysqlTokenType.UNKNOWN_TOKEN.ordinal();
        }
        return rights.get(dotPos);
    }

    public int getDotPos() {
        return dotPos;
    }

    public void setLookAheadSet(Set<Integer> lookAhead) {
        this.lookAhead = lookAhead;
    }

    public Set<Integer> getLookAhead() {
        return lookAhead;
    }

    public Production cloneSelf() {
        Production production = new Production(dotPos,left,rights,number);
        production.lookAhead = new HashSet<>();

        for (Integer symbol : this.lookAhead) {
            production.lookAhead.add(symbol);
        }
        return production;
    }


    public boolean canReduce(){
        if(dotPos>=rights.size()){
            return true;
        }

        for(int i=dotPos;i<rights.size();i++){
            if(!ProductionManager.getSelf().getFirstSetBuilder().isSymbolNullable(rights.get(i))){
                return false;
            }
        }

        //如果点号右边所有每个符号都可以推出null，那么也可以reduce
        return true;
    }

    public Production dotForward() {
        Production production = new Production(dotPos+1,left,rights,number);
        production.lookAhead = new HashSet<>();

        for (Integer symbol : this.lookAhead) {
            production.lookAhead.add(symbol);
        }
        return production;
    }


    /**
     * α, β, r 是 0个或多个终结符或非终结符的组合， x 是非终结符
     *
     * 已知表达式 s -> α .x β 的look ahead集合是C，即当前对象的lookAhead
     *
     * 求表达式 x -> . r 的look ahead 集合
     *
     *   first(β): 只要有一个β的symbol不是nullable
     *   C: β是空
     *   first(β) + C: β的所有symbol都是nullable的
     * @return
     */
    public Set<Integer> computeFirstSetOfBetaAndC() {
        List<Integer> β = new ArrayList<>();
        //计算β
        for(int i=dotPos+1;i<rights.size();i++){
            β.add(rights.get(i));
        }
        Set<Integer> lookAhead = new HashSet<>();

        //C: β是空,直接返回C，即lookAhead
        if(β.isEmpty()){
            lookAhead.addAll(this.lookAhead);
        }else{
            //计算first(β)
            for(int i=0;i<β.size();i++){
                int symbol = β.get(i);

                List<Integer> firstSet = ProductionManager.getSelf().getFirstSetBuilder().getFirstSet(symbol);
                for (Integer sym : firstSet) {
                    if(!lookAhead.contains(sym)){
                        lookAhead.add(sym);
                    }
                }

                //如果β中有一项不是nullable的，那就终止了，没必要计算β后面的
                if(!ProductionManager.getSelf().getFirstSetBuilder().isSymbolNullable(symbol)){
                    break;
                }

                //如果β所有项都是nullable的，那么把C加进来
                if(i==β.size()-1){
                    lookAhead.addAll(this.lookAhead);
                }
            }
        }

        return lookAhead;
    }


    @Override
    public boolean equals(Object obj){
        Production production = (Production) obj;
        if(productionEquals(production)&&lookAheadCompare(production)==0){
            return true;
        }
        return false;
    }

    /**
     * 比较表达式是否相等，左边，点号，右边
     * @param production
     * @return
     */
    public boolean productionEquals(Production production){
        if(production.getLeft()!=this.left){
            return false;
        }
        if(!production.getRights().equals(this.rights)){
            return false;
        }
        if(production.dotPos!=this.dotPos){
            return false;
        }
        return true;
    }

    /**
     * 看看当前的product的lookahead集合 是否全部包含传过来的product的lookahead集合
     * @param production
     * @return
     */
    public int lookAheadCompare(Production production){
        return this.lookAhead.equals(production.lookAhead)?0:(this.lookAhead.containsAll(production.lookAhead)?1:-1);
    }


    public void print() {

        StringBuilder sb = new StringBuilder();
        sb.append(MysqlTokenType.getSymbolStr(left)).append("->");

        for(int i=0;i<rights.size();i++){
            if(i==dotPos){
                sb.append(".");
            }
            sb.append(MysqlTokenType.getSymbolStr(rights.get(i))).append(" ");
        }
        if(dotPos==rights.size()){
            sb.append(".");
        }

        sb.append("   lookAhead=");
        for (Integer symbol : lookAhead) {
            sb.append(MysqlTokenType.getSymbolStr(symbol)).append(",");
        }
        System.out.println(sb);
    }

    //表达式相同，且1的lookahead 覆盖2的lookahead
    public boolean coverUp(Production product) {
        /*
         * 如果表达式相同，但是表达式1的look ahead 集合 覆盖了表达式2的look ahead 集合，
         * 那么表达式1 就覆盖 表达式2
         */
        if (this.productionEquals(product) && this.lookAheadCompare(product) > 0) {
            return true;
        }

        return false;
    }

    public Integer getNumber() {
        return number;
    }

}
